/*************************************************************************
	> File Name: dec.c
	> Author: Thinking
	> Mail: program_code@sohu.com 
	> Created Time: Wed 16 Jun 2021 05:45:46 AM PDT
 ************************************************************************/

#include <linux/kernel.h>			//Need for loglevels (KERN_WARNING KERN_INFO, KERN_EMERG)

#include <linux/module.h>			//Needed for all kernels modules
#include <linux/moduleparam.h>		

#include <linux/cdev.h>
#include <linux/fs.h>				//filp_open 
#include <linux/wait.h>				
#include <linux/poll.h>
#include <linux/sched.h>
#include <linux/slab.h>				//kmalloc

#include <linux/unistd.h>			//sys_call_table
#include <linux/syscalls.h>
#include <linux/string.h>
#include <linux/fs.h>
#include <linux/fdtable.h>
#include <linux/uaccess.h>
#include <linux/rtc.h>

#include <linux/kallsyms.h>
#include <linux/kprobes.h>
#include <linux/sched.h>
#include <linux/time.h>



#define PROC_V			"/proc/version"
#define BOOT_PATH		"/boot/System.map"
#define MAX_VERSION_LEN 256


unsigned long *syscall_table = NULL;


char* acquire_kernel_version(char* buf) {

	struct file *proc_version = NULL;
	char *kernel_version = NULL;

	
	mm_segment_t oldfs;

	oldfs = get_fs();

	set_fs(KERNEL_DS);
	
	proc_version = filp_open(PROC_V, O_RDONLY, 0);
	
	if (IS_ERR(proc_version) || (proc_version == NULL)) {

		set_fs(oldfs);
		printk(KERN_INFO,"file open file PRO_v");
		return NULL;
	}

	memset(buf,0,MAX_VERSION_LEN);

	vfs_read(proc_version, buf, MAX_VERSION_LEN, &(proc_version->f_pos));

	
	kernel_version = strsep(&buf, " ");
	kernel_version = strsep(&buf, " ");
	kernel_version = strsep(&buf, " ");

	filp_close(proc_version, 0);

	set_fs(oldfs);

	printk(KERN_INFO,"Kernel version: %s",kernel_version);

	return kernel_version;

}

static int find_sys_call_table(char* kern_ver) {

	char system_map_entry[MAX_VERSION_LEN] = {0x00};
	int i = 0;
	char* filename;

	size_t filename_length = strlen(kern_ver) + strlen(BOOT_PATH) + 1;
	
	struct file* f = NULL;
	
	mm_segment_t oldfs;

	if(kern_ver == NULL ) {

		return -1;		
	}

	oldfs = get_fs();

	set_fs(KERNEL_DS);

	printk(KERN_EMERG,"Kernel version: %s",kern_ver);

	filename = kmalloc(filename_length, GFP_KERNEL);

	if(filename == NULL) {

		printk(KERN_EMERG,"kmalloc faild on System Version");
		return -1;
	}

	memset(filename, 0, filename_length);
	strncpy(filename, BOOT_PATH, strlen(BOOT_PATH));
	strncat(filename, kern_ver, strlen(kern_ver));

	f = filp_open(filename, O_RDONLY, 0);

	if (IS_ERR(f) || (f == NULL)) {

		printk(KERN_EMERG, "Error Opening System.map-<version> file: %s", filename);
		return -1;
	}

	memset(system_map_entry, 0, MAX_VERSION_LEN);



	while(vfs_read(f, system_map_entry+i, 1, &f->f_pos) == 1) {

		if(system_map_entry[i] == '\n'|| i == MAX_VERSION_LEN) {
			
			i = 0;
			if (strstr(system_map_entry, "sys_call_table") != NULL) {

				char *sys_string;
				char *system_map_entry_ptr = system_map_entry;
				
				sys_string = kmalloc(MAX_VERSION_LEN, GFP_KERNEL);

				if(sys_string == NULL) {
					
					filp_close(f, 0);
					set_fs(oldfs);

					kfree(filename);
					return -1;
				}

				memset(sys_string, 0, MAX_VERSION_LEN);
				strncpy(sys_string, strsep(&system_map_entry_ptr, " "), MAX_VERSION_LEN);
				kstrtoul(sys_string, 16, &syscall_table);
				printk(KERN_EMERG, "syscall_table ret\n");

				kfree(sys_string);
				break;
			}
			
			memset(system_map_entry, 0, MAX_VERSION_LEN);
			continue;

		}
		i++;

	}
	
	filp_close(f, 0);

	set_fs(oldfs);


	kfree(filename);


	return 0;

}

int fileop_init(void)
{
	char* kernel_version = kmalloc(MAX_VERSION_LEN, GFP_KERNEL);

	printk(KERN_INFO " fileop driver init \n");

	int rc = find_sys_call_table(acquire_kernel_version(kernel_version));

	if (rc == -1) {

		printk(KERN_INFO, "find_sys_call_table error");
	}

	printk(KERN_INFO " fileop driver init ok \n");
	kfree(kernel_version);
	
    return 0;
}

void __exit fileop_exit(void)
{
	printk(KERN_INFO " fileop driver exit \n");
    return;

}
module_init(fileop_init);
module_exit(fileop_exit);
MODULE_LICENSE("GPL");
